/*	
v3
	after comparison between the pseudo_random functions	
chngd pseodo random

	*/

#include "misc.h"
#include <math.h>
#include <time.h>
#include <stdio.h>
#include "load_file.h"

/*PROTOTYPES*/
unsigned char bin2char(unsigned char bin);
unsigned short hexchar2bin(char c);


int MD5BasicHash(const unsigned char *in,int len, unsigned char *outBuf)
{

	MD5_CTX mdContext;

	MD5Init(&mdContext);
	MD5Update(&mdContext, (unsigned char *)in, len);
	MD5Final(&mdContext);

	memcpy(outBuf,mdContext.digest,MD5_OUTPUT_LENGTH_IN_BYTES);
	return MD5_OUTPUT_LENGTH_IN_BYTES;
}

int SHA1BasicHash (const unsigned char *in,int len, unsigned char *outBuf){

	int sharesult_value = 0;
	SHA1Context sha;

	SHA1Reset(&sha);
	SHA1Input(&sha, (const unsigned char *)in, len);
	sharesult_value = SHA1Result(&sha);
	if (!sharesult_value)
	{
		return 0;
	}
	else{
		memcpy(outBuf,sha.Message_Digest,SHA1_OUTPUT_LENGTH_IN_BYTES);
		return SHA1_OUTPUT_LENGTH_IN_BYTES;
	}
}

int cryptHash (BasicHashFunctionPtr cryptHashPtr, const char *passwd, unsigned char *outBuf)
{
	return cryptHashPtr((unsigned char *)passwd, strlen(passwd), outBuf);
}

int hexa2binary(const char *strIn, unsigned char *outBuf, int outMaxLen){
	int iw,i,hexlength; /*iw=index for writing */
	unsigned char bitblock; 
	i=0;

	while (strIn[i]!= '\0'){
		if ((strIn[i]<'0') || (strIn[i]>'f') || ((strIn[i]<'a') && (strIn[i]>'9')))
			return -1;
		i++;
	}
	hexlength = i;
	if ((outMaxLen*2)<i ) /*check for memory limit */
		return -1;

	iw=0; 
	for (i=0 ; (iw< outMaxLen) && (i<hexlength) ; i=i+2){
		bitblock = ( (hexchar2bin(strIn[i+1])<<4) | hexchar2bin(strIn[i]) );
		outBuf[iw] = bitblock; 
		iw++;
	}
	if (iw<outMaxLen) /*fill zero's if the input ended early */
		for (; (iw< outMaxLen) ; iw++)
			outBuf[iw]= 0;
	return iw;
}

int binary2hexa(const unsigned char *bufIn, int lengthIn,
		char *outStr, int outMaxLen){
	unsigned char temp;
	int i,iw; /*i - read index, iw - write index */

	i=0;
	iw=0;
	while (( (outMaxLen-1) >(iw+1)) && (lengthIn>i) ){ /* the (outMaxLen-1) is to keep space for Null terminated */
		temp = bufIn[i];
		outStr[iw] = bin2char(temp & 0xf); /* first 4 bits */
		iw++;
		outStr[iw] = bin2char(temp>>4); /* second 4 bits */
		iw++;
		i++;
	}
	if (( (outMaxLen-1) >iw) && (lengthIn>i) ){ /*check if we can still write one more 0x digit */
		temp = bufIn[i];
		outStr[iw] = bin2char(temp & 0xf); /*first 4 bits */
		iw++;
		outStr[iw] ='\0';
		if ( (temp>>4) != 0) /*there are more 0x digits, but no space */
			return -1;
		else
			i++;
	}
	else { /*there is only room for the Null ending, or input was over */
		outStr[iw] = '\0';
	}

	if (lengthIn == i)
		return i;
	else
		return -1;

}

LONG_INDEX_PROJ pseudo_random_generator_proj(int step)
{
	LONG_INDEX_PROJ hashedBuffer[MD5_OUTPUT_LENGTH_IN_BYTES/sizeof(LONG_INDEX_PROJ)];
	int tmp2hashBuf[2];
	tmp2hashBuf[0] = step; /*make sure step affects result*/
	tmp2hashBuf[1] = 0xafda0c; /*make your program different from other*/
	/*Now, hash 'step' into a pseudo-random 16Bytes buffer:*/
	MD5BasicHash ( (unsigned char *)tmp2hashBuf,
			2*sizeof(int),(unsigned char *)hashedBuffer);
	/*now re-interpret first 8 bytes in hashedBuffer as result, kill sign bit*/
	return ((hashedBuffer[0])&0x7fffffffffffffff);
}



LONG_INDEX_PROJ pseudo_random_function(const unsigned char *x,int inputLength,LONG_INDEX_PROJ seed){

	unsigned char *buffer = NULL;
	unsigned char *outbuffer = NULL;
	LONG_INDEX_PROJ outcome = 1;

	if (x==NULL){
		return 0;
	}

	buffer = (unsigned char *) calloc(sizeof(LONG_INDEX_PROJ)+inputLength, sizeof(unsigned char) );
	if (buffer==NULL){
		fprintf(stderr,"\n failed memory alloc. : pseudo_random_function\n");
		return 0;
	}

	outbuffer = (unsigned char *) calloc(MD5_OUTPUT_LENGTH_IN_BYTES, sizeof(unsigned char) );
	if (outbuffer==NULL){
		free(buffer);
		fprintf(stderr,"\n failed memory alloc. : pseudo_random_function\n");
		return 0;
	}

	keyCpy(buffer ,(unsigned char *) &seed, sizeof(LONG_INDEX_PROJ));

	memcpy(&(buffer[sizeof(LONG_INDEX_PROJ)]) ,x, inputLength);

	MD5BasicHash(buffer, sizeof(LONG_INDEX_PROJ)+inputLength , outbuffer);

	outcome = *( (LONG_INDEX_PROJ *) &outbuffer[4] ); 

	free(buffer);
	free(outbuffer);
	return outcome;
}

int verifyHexa(char *str){
	char *c = NULL;

	if (str == NULL) return 0;
	c = str;
	while (*c != '\0'){
		if ((*c < '0' || *c > '9') && (*c < 'a' || *c > 'f'))
			return 0;
		c++;
	}
	return 1;
}

void readBuffer(char *buff){
	int c = 0;
	int i = 0;

	while (((c = getchar()) != '\n') && (i < 79) ){
		*(buff + i++) = c;
	}
	*(buff + i) = '\0';
}

void turnUpper(char *str){
	while (*str){
		*str = toupper(*str);
		str++;
	}
}

/*reads a line from file*/
int fgetLine(FILE *file, char *buffer){
	char c = 'a';
	int i = 0;

	while (((c = getc(file)) != EOF) && (c != '\n')){
		*(buffer+i) = c;
		i++;
	}
	*(buffer+i+1) = '\0';
	if (c == EOF){return 0;}
	else return 1;
}

/*this function assumes there is only one hash f. for a file
 * which means there is a line with it's name in the file
 * and all other lines are LEGAL records of <user>\t<password> format
 * first finds out the number of records in the file. then populates an array with the info
 * it's use is dual through the saltFlag argument which operates as a boolean
 * indicating if the file was created with salt

  note: on mem. allocation error we don't free the whole array since we have a different code for that in the right places
	of part 1 exe files!!--------this function is not used outside of part 1.
 */
int createfArr(char *filename, record **rArr, BasicHashFunctionPtr *h, int *asize,int *hsize, int saltFlag){
	FILE *in = NULL;
	char buffer[180] = {0};
	int ret = 0, i = 0;
	long pos = 0;
	record *r = NULL;

	in = fopen(filename,"r");
	if (in == NULL) return 0;

	ret = fgetLine(in, buffer);
	if (ret == 0){
		fclose(in);
		return 0;
	}

	parseHashFunction(buffer, h, hsize);
	if (h == NULL){
		fclose(in);
		return 0;
	}

	pos = ftell(in);
	*asize = getNumLines(in);
	r = (record *)malloc((*asize)*sizeof(record));
	if (r == NULL){
		fclose(in);
		return -7;
	}
	fseek(in, pos, 0);
	for (i = 0;i < *asize; i++){
		ret = fgetLine(in, buffer);
		r[i].name = NULL;
		r[i].pswd = NULL;
		if (parseLine(&r[i], buffer, *hsize, saltFlag) == -7){
			if (r[i].name != NULL)
				free(r[i].name);
			if (r[i].pswd != NULL)
				free(r[i].pswd);
		}
	}
	*rArr = r;
	fclose(in);
	return 1;
}

/*a function that receives a line and parses it to the record
 * allocating the mem. needed saltFlag is a boolean identifier used
 * to check wether a salt field is in the buffer

	this function like createfArr is used only by part 1
 */
int parseLine(record *r, char *buffer,int hsize, int saltFlag){
	char *b = NULL;
	int size = 0, i = 0;

	b = buffer;
	while (*b != '\t'){
		++b;
		size++;
	}
	r->name = (char *)calloc(size+1, sizeof(char));
	if (r->name == NULL) {
		return -7;
	}
	b = r->name;
	while (*buffer != '\t'){
		*b = *buffer;
		b++;
		buffer++;
	}
	*b = '\0';

	r->pswd = (char *)calloc((hsize*2)+1, sizeof(char));
	if (r->pswd == NULL){
		free(r->name);
		return -7;
	}
	b = r->pswd;
	buffer++;
	for (i = 0; i < (2*hsize); i++){
		*b = *buffer;
		b++;
		buffer++;
	}
	*b = '\0';

	if (saltFlag == 0)
		return 1;
	else {
		r->salt[6] = '\0';
		for (i = 0; i < 6; i++){
			r->salt[i] = *buffer;
			buffer++;
		}

		return 1;
	}
}

int getNumLines(FILE *in){
	int counter = 0;
	char a = 0;

	while ((a = getc(in)) != EOF){
		if (a == '\n'){
			counter++;
		}
	}
	return counter;
}

/*this function is used to free the array created with freefArr*/
void freefArr(record **r, int asize, int saltFlag){
	int i = 0;

	for (i = 0; i < asize; i++){
		if ((*r)[i].name != NULL)
			free((*r)[i].name);
		if ((*r)[i].pswd != NULL)
			free((*r)[i].pswd);
	}

	free(*r);
}

/* random 6 chars of salt - no control chars */
void createSalt(char *saltBuffer){
	/* Length of the salt */
	unsigned short int length = 6;

	/* ASCII characters 33 to 126 */
	while(length--)
	{
		*saltBuffer = ( (rand() % 93) + 33);
		saltBuffer++;

	}

	return;
}

unsigned short hexchar2bin(char c) {
	if (c=='\0') return 0;
	if (c=='0') return 0;
	if (c=='1') return 1;
	if (c=='2') return 2;
	if (c=='3') return 3;
	if (c=='4') return 4;
	if (c=='5') return 5;
	if (c=='6') return 6;
	if (c=='7') return 7;
	if (c=='8') return 8;
	if (c=='9') return 9;
	if (c=='a') return 10;
	if (c=='b') return 11;
	if (c=='c') return 12;
	if (c=='d') return 13;
	if (c=='e') return 14;
	if (c=='f') return 15;
	else return 0;
}

unsigned char bin2char(unsigned char bin){
	if (bin>15){
		/*check for bad binary */
		return 0;
	}
	if (bin < 10)
		return (bin+48); /*bin + '0' */
	else
		return (bin+87); /*bin + 'a' - 10 */
}

/*this is used only by part 1 for checking a command line buffer read from user*/
int checkBuffer(char *buffer){
	char *p = NULL;

	if (buffer == NULL) return 0;
	
	p = buffer;	
	while ((*p != '\0') && (*p != '\t')){	
		if ( (*p < 32) ||  (*p > 126) )
			return 0;		
		p++;
	}
	
	if (!(*p=='\t'))
		return 0;
	p++;

	while ((*p >= 32) && (*p <= 126)){
		p++;
	}
	if (*p != '\0'){
		return 0;
	}
	return 1;
}


/*parser for hash function pointer and size param.*/
void parseHashFunction(char *functionName, BasicHashFunctionPtr *h, int *hSize){
	char buffer[80] = {0};

	strcpy(buffer, functionName);
	turnUpper(buffer);
	if (strcmp(buffer,"SHA1") == 0){
		*h = SHA1BasicHash;
		*hSize = SHA1_OUTPUT_LENGTH_IN_BYTES;
	}
	else if (strcmp(buffer,"MD5") == 0){
		*h = MD5BasicHash;
		*hSize = MD5_OUTPUT_LENGTH_IN_BYTES;
	}
	else {
		*h = NULL;
		*hSize = 0;
	}
}

int checkRule(char *ruleStr){
	char *p = NULL;
	int min = 0, max = 0;

	if (ruleStr == NULL)
		return 0;
	if ((*ruleStr == '\0') || (*ruleStr == '|'))
		return 0;
	p = ruleStr;
	
	while (*p != '\0'){
		if ((*p == '#') || (*p == '$') || (*p == '@')){
			p++;
		}
		else if (*p == '|'){
			p++;
			if ((*p == '|') || (*p=='\0'))
				return 0;
		}
		else if ((*p == '!') || (*p == '?') || (*p == '&') || (*p == '%')){
			p++;
			if (!isdigit(*p))
				return 0;
			min = atoi(p);
			while (isdigit(*p)){
				p++;
			}
			if (*p != '-') return 0;
			p++;
			if (!isdigit(*p))
				return 0;
			max = atoi(p);
			while (isdigit(*p)){
				p++;
			}
			if (min > max) return 0;		
		}
		else {
			return 0;
		}
	}
	return 1;
}


int keyCmp(const unsigned char *s1, const unsigned char *s2, int byteInValidationKey){
	const unsigned char *a = NULL;
	const unsigned char *b = NULL;
	int i = 0;

	a = s1;
	b = s2;

	for (i = 0; i < byteInValidationKey; i++){
		if (a[i] != b[i]) return 1;
	}
	return 0;
}

int keyCpy(unsigned char *s1, unsigned char *s2, int byteInValidationKey){
	unsigned char *a = NULL;
	unsigned char *b = NULL;
	int i = 0;

	a = s1;
	b = s2;

	for (i = 0; i < byteInValidationKey; i++){
		a[i] = b[i];
	}
	return 1;
}
